package middleware.llvm.instruction;

import middleware.llvm.value.ArrayIRType;
import middleware.llvm.value.BasicIRType;
import middleware.llvm.value.IRType;
import middleware.llvm.value.Value;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

/**
 * 表示获取数组元素指针的指令
 *
 * @author 杨凯伟
 * @date 2024/10/30
 */
public class GetElementPtrInstruction extends Instruction {
    private Value elementBase;
    private final List<Value> offsets = new ArrayList<>();

    public Value getElementBase() {
        return elementBase;
    }

    public List<Value> getOffsets() {
        return offsets;
    }

    public GetElementPtrInstruction(Value elementBase, List<Value> offsets) {
        super(getGEPInstType(elementBase.getType(), offsets.size()),
                buildOperands(elementBase, offsets).toArray(new Value[0]));

        this.elementBase = elementBase;
        this.offsets.addAll(offsets);
    }

    private static List<Value> buildOperands(Value elementBase, List<Value> offsets) {
        List<Value> rt = new ArrayList<>();
        rt.add(elementBase);
        rt.addAll(offsets);
        return rt;
    }

    private static IRType getGEPInstType(IRType elementBaseType, int offsetCount) {
        if (elementBaseType instanceof ArrayIRType arrayIRType) {
            var oriArrayDims = arrayIRType.getArrayDims();
            var retDims = oriArrayDims.subList(offsetCount - 1, oriArrayDims.size());
            if (retDims.isEmpty()) {
                return new BasicIRType(arrayIRType.getElmType().getType()).ptr(1);
            } else {
                return new ArrayIRType(arrayIRType.getElmType(), retDims).ptr(1);
            }
        } else {
//            assert offsetCount == 1;
            return elementBaseType.clone();
        }
    }


    @Override
    public void replaceOperand(int pos, Value newOperand) {
        super.replaceOperand(pos, newOperand);
        if (pos == 0)
            elementBase = newOperand;
        else
            offsets.set(pos - 1, newOperand);
    }

    @Override
    public void dump(PrintStream out) {
        var dataType = elementBase.getType().clone().ptr(elementBase.getType().getPtrNum() - 1);
        out.printf("  %s = getelementptr %s, %s",
                this.getName(),
                dataType,
                elementBase);

//        for (Value offset : offsets) {
//            out.printf(", %s", offset.toString());
//        }
        offsets.forEach(offset -> out.printf(", %s", offset.toString()));
        out.print("\n");
    }


}
